#include "common.h"
#include "ftp.h"
#include <stack>

static void ftp_cli(int sockfd,const char* username,const char *password,const char* out_file);
static int get_one_dir(int sockfd,string &top_item,FILE *fp_out_file,stack<string> &dir_stack);
static int set_addr(struct sockaddr_in*,char *);
static int is_dir_line(const char *buf);
static char *parse_dir_name(char *recvbuf);

/**
 * get file names of one ftp site.
 * @argv[1] - ip address
 * @argv[2] - port number
 * @argv[3] - account
 * @argv[4] - password
 * @argv[5] - output file name
 */
int main(int argc,char *argv[]) {

	int sockfd;
	struct sockaddr_in servaddr;
	short int portnum;
	int ret;
	//char out_file[MAXNAME];

	if(argc != 6) {
		//printf("usage:%s <ip> <port> <account> <password> <filename>\n",argv[0]);
		MSG("argc error:getOneSite");
		exit(EXIT_FAILURE);
	}

	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1) {
		MSG("socket failed");
		exit(EXIT_FAILURE);
	}

	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	portnum = atoi(argv[2]);
	if(portnum == 0) {
		MSG("invalid port number:%s",argv[2]);
		exit(EXIT_FAILURE);
	}
	servaddr.sin_port = htons(portnum);
	ret = inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
	if(ret == -1) {
		MSG("inet_pton failed:%s",argv[1]);
		exit(EXIT_FAILURE);
	}

	ret = connect_timeout(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
	if(ret == -1) {
		MSG("connect failed:%s %s",argv[1],argv[2]);
		exit(EXIT_FAILURE);
	}
	
	//snprintf(out_file,MAXNAME,"%s_%s",argv[1],argv[2]);
	ftp_cli(sockfd,argv[3],argv[4],argv[5]);

	ret = close(sockfd);
	if(ret == -1) {
		MSG("close failed");
		exit(EXIT_FAILURE);
	}

	run_log("getOneSite finished : %s %s %s %s",argv[1],argv[2],argv[3],argv[4]);
	exit(EXIT_SUCCESS);
}

static void ftp_cli(int sockfd,const char *username,const char *password,const char* out_file) {
	char sendbuf[MAXLINE];
	FILE *fp_out_file;
	stack<string> dir_stack;
	string top_item;	
	int code;
	int ret;
	
	code = res_from_server(sockfd,NULL,0);
	if(code != 220) {
		MSG("server not ready for new user");
		exit(EXIT_FAILURE);
	}

	snprintf(sendbuf,MAXLINE,"%s %s\r\n",CLI_USER,username);
	ret = cmd_to_server(sockfd,sendbuf);
	if(ret == -1) {
		MSG("cmd_to_server failed:USER %s",username);
		exit(EXIT_FAILURE);
	}

	code = res_from_server(sockfd,NULL,0);
	if(code != 331) {
		MSG("username error:%s",username);
		exit(EXIT_FAILURE);
	}

	snprintf(sendbuf,MAXLINE,"%s %s\r\n",CLI_PASS,password);
	ret = cmd_to_server(sockfd,sendbuf);
	if(ret == -1) {
		MSG("cmd_to_server failed:PASS %s",password);
		exit(EXIT_FAILURE);
	}

	code = res_from_server(sockfd,NULL,0);
	if(code != 230) {
		MSG("username or password error,not logged in");
		exit(EXIT_FAILURE);
	}

	/*
	snprintf(sendbuf,MAXLINE,"%s\r\n",CLI_SYST);
	cmd_to_server(sockfd,sendbuf);

	code = res_from_server(sockfd,NULL,0);
	if(code != 215) {
		MSG("SYST failed");
		exit(EXIT_FAILURE);
	}
	*/

	fp_out_file = fopen(out_file,"w+");
	dir_stack.push("/");
	while(!dir_stack.empty()) {
		top_item = dir_stack.top();
		dir_stack.pop();
		MSG("top:%s\n",top_item.c_str());
		fprintf(fp_out_file,"%s:\n",top_item.c_str());
		
		ret = get_one_dir(sockfd,top_item,fp_out_file,dir_stack);
		if(ret == -1) {
			MSG("get_one_dir failed:%s",top_item.c_str());
			exit(EXIT_FAILURE);
		}

		fprintf(fp_out_file,"\n");
	}

	fclose(fp_out_file);

	/*
	snprintf(sendbuf,MAXLINE,"%s\r\n",CLI_QUIT);
	ret = cmd_to_server(sockfd,sendbuf);
	if(ret == -1) {
		MSG("cmd_to_server failed:%s",CLI_QUIT);
		exit(EXIT_FAILURE);
	}

	ret = res_from_server(sockfd,NULL,0);
	if(ret == -1) {
		MSG("res_from_server failed");
		exit(EXIT_FAILURE);
	}
	*/

}

static int get_one_dir(int sockfd,string &top_item,FILE *fp_out_file,stack<string> &dir_stack) {
	int datafd;
	struct sockaddr_in dataaddr;
	char sendbuf[MAXLINE];
	char recvbuf[MAXLINE];
	int code;
	int ret;

	snprintf(sendbuf,MAXLINE,"%s\r\n",CLI_PASV);
	ret = cmd_to_server(sockfd,sendbuf);
	if(ret == -1) {
		MSG("cmd_to_server failed:%s",CLI_PASV);
		return -1;
	}

	/*
	code = res_from_server(sockfd,recvbuf,MAXLINE);
	if(code < 0) {
		MSG("res_from_server error")
		return -1;
	}
	else if(code != 227) {
		MSG("failed to enter passive mode");
		return -1;
	}
	*/
	do {
		code = res_from_server(sockfd,recvbuf,MAXLINE);
		if(code < 0) {
			MSG("res_from_server error")
			return -1;
		}
	} while(code != 227);

	
	datafd = socket(AF_INET,SOCK_STREAM,0);
	if(datafd == -1) {
		MSG("socket failed");
		return -1;
	}

	bzero(&dataaddr,sizeof(dataaddr));
	ret = set_addr(&dataaddr,recvbuf);
	if(ret == -1) {
		MSG("set_addr failed");
		return -1;
	}

	ret = connect_timeout(datafd,(struct sockaddr *)&dataaddr,sizeof(dataaddr));
	if(ret == -1) {
		MSG("connect failed:%s",inet_ntoa(dataaddr.sin_addr));
		return -1;
	}

	snprintf(sendbuf,MAXLINE,"%s %s\r\n",CLI_LIST,top_item.c_str());
	ret = cmd_to_server(sockfd,sendbuf);
	if(ret == -1) {
		MSG("cmd_to_server failed:%s %s",CLI_LIST,top_item.c_str());
		return -1;
	}

	ret = res_from_server(sockfd,NULL,0);
	if(ret == -1) {
		MSG("res_from_server failed");
		return -1;
	}

	// where should I be?
	#if 0
	ret = res_from_server(sockfd,NULL,0);
	if(ret == -1) {
		MSG("res_from_server failed");
		return -1;
	}
	#endif

	while(1) {

		ret = readline_timeout(datafd,recvbuf,MAXLINE);
		if(ret == 0) {
			MSG("datafd reach EOF");
			break;
		}
		else if(ret == -1) {
			MSG("readline_timeout failed:get_one_dir");
			return -1;
		}

		fprintf(fp_out_file,"%s",recvbuf);
		run_log("datafd - %s",recvbuf);
		if(is_dir_line(recvbuf)) {
			char *tmp_name = parse_dir_name(recvbuf);
			//printf("(tmp_name = %s)\n",tmp_name);
			run_log("dir name:(%s)",tmp_name);
			if(strcmp(tmp_name,".") == 0 || 
			   strcmp(tmp_name,"..") == 0 ||
			   strcmp(tmp_name,"") == 0)
				continue;

			string tmp_str(tmp_name);
			tmp_str = top_item + tmp_str + "/";
			dir_stack.push(tmp_str);
		}

	}

	
	/*
	ret = res_from_server(sockfd,NULL,0);
	if(ret == -1) {
		MSG("res_from_server failed");
		return -1;
	}
	*/

	MSG("Hello");

	ret = close(datafd);
	if(ret == -1) {
		MSG("close failed");
		return -1;
	}
	run_log("get_one_dir finished:%s",top_item.c_str());

	return 0;
	
}

static int set_addr(struct sockaddr_in* paddr,char *response) {
	char *ptr = strchr(response,'(');
	char addr[MAXNAME];
	int port_num;
	int n1,n2,n3,n4,n5,n6;
	int ret;

	sscanf(ptr,"(%d,%d,%d,%d,%d,%d)",&n1,&n2,&n3,&n4,&n5,&n6);
	snprintf(addr,MAXNAME,"%d.%d.%d.%d",n1,n2,n3,n4);
	port_num = n5*256 + n6;
	MSG("set_addr - %s - %d",addr,port_num);

	paddr->sin_family = AF_INET;
	paddr->sin_port = htons(port_num);
	ret = inet_pton(AF_INET,addr,&(paddr->sin_addr));
	if(ret == -1) {
		MSG("inet_pton failed:%s",addr);
		return -1;
	}

	return 0;
}

static char *parse_dir_name(char *recvbuf) {
	/*
	char *t1 = strrchr(recvbuf,' ') + 1;
	char *t2 = strrchr(recvbuf,'\r');
	*t2 = 0;
	return t1;
	*/
	int num;
	int i;
	char *tmp;
	
	tmp = strrchr(recvbuf,'\r');
	*tmp = 0;
	tmp = recvbuf;

	if(isdigit(recvbuf[0])) {
		num = 3;
		for(i = 0; i < num; i++) {
			while(!isspace(*++tmp));
			while(isspace(*++tmp));
		}

	}
	else {
		num = 8;
		for(i = 0; i < num; i++) {
			while(!isspace(*++tmp));
			while(isspace(*++tmp));
		}

	}

	return tmp;

}


static int is_dir_line(const char *buf) {
	int num;
	int i;
	const char *tmp = buf;

	if(isdigit(buf[0])) {
		num = 2;
		for(i = 0; i < num; i++) {
			while(!isspace(*++tmp));
			while(isspace(*++tmp));
		}
		//printf("tmp=(%s)\n",tmp);

		if(strncasecmp(tmp,DOS_DIR,strlen(DOS_DIR)) == 0)
			return 1;
	}
	else if(buf[0] == 'd')
		return 1;
			
	return 0;
}
